// switches.c - Fucntions for advanced/non-standard switches

#include <string.h>

#include <stdbool.h>
#include "os.h"

#include "dir.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

//+**************************************************************************
// Returns:  0 - doesn't match
//          !0 - matches
//
// Your Mission, should you Decide To Accept It, is to Unravel The Code
//
// Based on:
//   http://www.schweikhardt.net/#ioccc
//     Jens Schweikhardt winner Best one-liner 2001 IOCCC
//
// Greetz to: prec  on  irc.freenode.net/##C
//
//***************************************************************************

int     glob(const char *mask, const char *fname)
{
  return ( (*mask!='*') ? (!*fname) ? (!*mask)
                                    : (*mask=='?' || *fname==*mask) &&
                                      glob(mask+1, fname+1)
                        : glob(mask+1, fname) ||
                          (*fname && glob(mask, fname+1)) );
}

//+**************************************************************************
// dir( root, recurse, process )
//
// Entries and NOT sorted
// Directories are process'ed BEFORE files
// The search is DEPTH-first 
//   Ie. When we find a directory-entry, we enter & process it immediately
//       and come back for the other dir/file-entries later.
//
// root    is a string containing the root directory
// recurse is true if you wish to recurse into subdirectories
// process is the function which will be called for each entry
//
// The function declaration for process() is:
//   int  process( int depth, char* path, char* rpath, char* name,
//                 struct stat* stat, bool dir, void* prams )
//
// 'depth' of 0 is the root directory
// 'path'  will start with the supplied 'root'
//         will always supply slashes of type DIRSLASH, as set in os.h
//         will always finish with a DIRSLASH
// 'rpath' is a pointer within 'path' which identifies where the
//         relative path starts.  If there is no relative path (ie. the file
//         passed is in root) 'rpath' will point to the terminating '\0' of
//         'path'
// 'name'  the <name> of the file or directory entry (with extension)
// 'stat'  a pointer to the file/directories stat entry
//         for this you will need to: #include <sys/stat.h>
// 'dir'   true => entry is a directory,  false => entry is a file
// 'prams' a pointer to anything you would like to pass to your process()
//
// Your process() MUST return zero to ensure the dir continues
// Your process() MAY return a non-zero value to terminate the dir mid-way
//                This value will be the return value of dir()
//
//***************************************************************************

int     dir( char* root, bool recurse,
             int (*doit)(int,char*,char*,char*,struct stat*,bool,void*), void* prams )
{

char    path[dirNameMax];
char*   cp;

  if ((strlen(root)+2)>dirNameMax)  return(dirROOTTOOLONG) ;
  // Take a copy of the root directory name, so it can grow
  strcpy(path, root);
  // convert all the slashes to be the correct format for this platform
  for (cp=path; *cp; cp++)  if ( (*cp=='/') || (*cp=='\\') )  *cp = *DIRSLASH ;
  // append a DIRSLASH if required
  if (path[strlen(path)-1]!=*DIRSLASH)  strcat(path, DIRSLASH) ;
  // and they're off...
  return( dir_rec(path, path +strlen(path), dirNameMax, recurse, 0, doit, prams) );

}


//+************************************

int     dir_rec( char* path, char* rpath, int max, bool recurse, int depth,
                 int (*doit)(int,char*,char*,char*,struct stat*,bool,void*), void* prams )
{

DIR*            dir;
struct  dirent* dp;
struct  stat    fs;

int     len;
int     err;

int     pass;

  // Windows won't allow you to open this folder!
  if (!strcmp(path, "\\System Volume Information\\"))  return(0) ;

  // useful throughtout
  len = strlen(path);

  // If recursing, do ODD iteration first
  for (pass = (recurse?1:0); pass<(recurse?3:2); pass++)
  {
    if ( !(dir = opendir(path)) )  return(dirBADROOT) ;  // open root

    while ((dp = readdir(dir)))  // read all directory entries
    {
      // check for buffer overflows  // +1 for '\0', +1 for DIRSLASH (later)
      if ((len +strlen(dp->d_name) +1 +1) >= max)  return(dirTOOLONG) ;
      strcat(path, dp->d_name);       // make full dir/filename
      stat(path, &fs);                // get dir entry
      path[len] = '\0';               // remove dir/filename

      if (fs.st_mode &S_IFDIR)  // It's a directory
      {
        if (pass&1) // process directories on ODD iteration
          // Do not process "." and ".."
          //if (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))
          if ( (dp->d_name[0]!='.' ) ||
               ( (dp->d_name[1]!='\0') &&
                 (dp->d_name[1]!='.' || dp->d_name[2]!='\0') ) )
          {
            // execute the user function for this entry
            // will return non-zero to cause dir() to terminate
            if ((err = doit(depth, path, rpath, dp->d_name, &fs, true, prams)))
              return(err);
            if (recurse)
            {
              strcat(path, dp->d_name);  // Append this dir to the current path
              strcat(path, DIRSLASH);
              if ((err = dir_rec(path, rpath, max, recurse, depth+1, doit, prams)))
                return(err);  // throw
              path[len] = '\0'; // remove this dir from the path
            }
          }
      }
      else  // It's a file
      {
        if (!(pass&1)) // process files on EVEN iteration
          // execute the user function for this entry
          // will return non-zero to cause dir() to terminate
          if ((err = doit(depth, path, rpath, dp->d_name, &fs, false, prams)))
            return(err);
      }
    } // next entry

    // close up the directory
    (void)closedir(dir);
  } // next pass

  return(0);

}


// dir.c [EOF]
